package com.hzw.saas.service.storage.util;

import java.util.List;
import java.util.Objects;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.hzw.saas.api.storage.bo.ResourceFileStorageBO;
import com.hzw.saas.api.storage.constants.StorageCacheConsts;
import com.hzw.saas.api.storage.constants.StorageConsts;
import com.hzw.saas.api.storage.enums.StorageSpaceEnum;
import com.hzw.saas.common.config.query.LogicQuery;
import com.hzw.saas.common.config.util.AssertUtil;
import com.hzw.saas.common.storage.pojo.FileStorage;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.common.util.constants.CacheConsts;
import com.hzw.saas.service.storage.model.Resource;
import com.hzw.saas.service.storage.model.ResourceFile;
import cn.hutool.core.text.StrSplitter;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestAlgorithm;

/**
 * @author zzl
 * @since 02/25/2021
 */
public class StorageUtil {

    /**
     * 判断是否为rootId (存储空间根目录ID)
     * 即数据库中的用户各存储空间根目录ID，唯一
     * <p>
     * (userId & spaceId) 唯一确定一个存储空间根目录ID
     *
     * @param pid 目录ID
     * @return true or false
     */
    public static boolean isRootId(String pid) {
        return pid.startsWith(StorageConsts.ROOT_ID_PREFIX);
    }

    /**
     * 获取存储空间根目录ID
     * format: ROOT-{spaceId}-{userId}
     * <p>
     * 即数据库中的用户各存储空间根目录ID，唯一
     * <p>
     * (userId & spaceId) 唯一确定一个存储空间根目录ID
     *
     * @param userId  用户ID
     * @param spaceId 存储空间ID，即ApiRootId
     * @return 存储根目录ID
     * @see StorageSpaceEnum
     */
    public static String getRootId(String userId, String spaceId) {
        return StrUtil.concat(true, StorageConsts.ROOT_ID_PREFIX, spaceId, StorageConsts.ROOT_ID_FACTOR, userId);
    }

    /**
     * 解析API中传入的目录ID
     * 若为spaceId，则转换为rootId，否则返回自身
     *
     * @param userId            用户ID
     * @param folderIdOrSpaceId folder id, 可能为api-rootId
     * @return rootId
     */
    public static String getProperFolderId(String userId, String folderIdOrSpaceId) {
        return isSpaceId(folderIdOrSpaceId) ? getRootId(userId, folderIdOrSpaceId) : folderIdOrSpaceId;
    }

    /**
     * 判断是否为spaceId (WEB-API传递的根目录ID)
     * WEB-API中使用spaceId作为用户存储空间的的根目录ID
     * <p>
     * 因为web接口中总会传入用户信息，且(userId & spaceId) 唯一确定一个存储空间根目录ID，
     * 则可以通过通用的spaceId来代替复杂的storage-rootId
     *
     * @param pid 目录ID
     * @return true or false
     */
    public static boolean isSpaceId(String pid) {
        return StorageSpaceEnum.isValid(pid);
    }

    /**
     * 根据存储根目录ID解析存储空间ID和用户ID
     *
     * @param rootId 存储空间根目录ID
     * @return inx0: 存储空间ID，inx1: 用户ID
     */
    public static String[] parseRootId(String rootId) {
        String[] result = new String[]{"", ""};
        if (StrUtil.isBlank(rootId) || !isRootId(rootId))
            return result;
        List<String> strings = StrSplitter.splitTrim(rootId, StorageConsts.ROOT_ID_FACTOR, true);
        if (Objects.equals(strings.size(), 3)) {
            result[0] = strings.get(1);
            result[1] = strings.get(2);
        }
        return result;
    }

    /**
     * 根据存储根目录ID解析存储空间ID
     *
     * @param rootId 存储空间根目录ID
     * @return 存储空间ID
     */
    public static String parseSpaceIdByRootId(String rootId) {
        if (StrUtil.isBlank(rootId) || !isRootId(rootId))
            return StrUtil.EMPTY;
        List<String> strings = StrSplitter.splitTrim(rootId, StorageConsts.ROOT_ID_FACTOR, true);
        return Objects.equals(strings.size(), 3) ? strings.get(1) : StrUtil.EMPTY;
    }

    /**
     * 根据存储根目录ID解析存储用户ID
     *
     * @param rootId 存储空间根目录ID
     * @return 用户ID
     */
    public static String parseUserIdByRootId(String rootId) {
        if (StrUtil.isBlank(rootId) || !isRootId(rootId))
            return StrUtil.EMPTY;
        List<String> strings = StrSplitter.splitTrim(rootId, StorageConsts.ROOT_ID_FACTOR, true);
        return Objects.equals(strings.size(), 3) ? strings.get(2) : StrUtil.EMPTY;
    }

    /**
     * 获取fileId对应的同步任务缓存分组key
     *
     * @param fileId
     * @return sync task cache group key
     */
    public static String getSyncTaskCacheGroupKey(String fileId) {
        return StrUtil.concat(false, StorageCacheConsts.RSYNC_TASK_CACHE_PREFIX, fileId);
    }

    /**
     * 生成同步任务缓存key，同时也是syncId
     *
     * @param fileId
     * @return sync task cache key (syncId)
     */
    public static String generateSyncTaskCacheKey(String fileId) {
        String keyJoiner = CacheConsts.CACHE_KEY_JOINER;
        String keyGroup = getSyncTaskCacheGroupKey(fileId);
        return StrUtil.concat(false, keyGroup, keyJoiner, SnowFlakeUtil.nextIdStr());
    }

    /**
     * 根据syncId解析fileId
     *
     * @param syncId 同步任务ID
     * @return fileId
     */
    public static String parseFileIdBySyncId(String syncId) {
        if (StrUtil.isBlank(syncId))
            return StrUtil.EMPTY;
        AssertUtil.assertThrow("syncId异常", !syncId.startsWith(StorageCacheConsts.RSYNC_TASK_CACHE_PREFIX));
        int startIndex = StorageCacheConsts.RSYNC_TASK_CACHE_PREFIX.length();
        int endIndex = syncId.indexOf(CacheConsts.CACHE_KEY_JOINER, StorageCacheConsts.RSYNC_TASK_CACHE_PREFIX.length());
        return syncId.substring(startIndex, endIndex);
    }

    /**
     * 获取fileId对应的分块校验和缓存分组Key
     *
     * @param fileId
     * @return cache group key
     */
    public static String getBlockChecksumCacheGroupKey(String fileId) {
        return StrUtil.concat(false, StorageCacheConsts.RSYNC_CHECKSUM_CACHE_PREFIX, fileId);
    }

    /**
     * 获取fileId对应的分块校验和缓存key
     *
     * @param fileId
     * @param blockLength
     * @param blockStep
     * @return cache key
     */
    public static String getBLockChecksumCacheKey(String fileId, Integer blockLength, Integer blockStep) {
        String keyJoiner = CacheConsts.CACHE_KEY_JOINER;
        String keyGroup = getBlockChecksumCacheGroupKey(fileId);
        return StrUtil.concat(false, keyGroup, keyJoiner, blockLength.toString(), keyJoiner, blockStep.toString());
    }

    /**
     * 转换文件信息->文件存储信息
     *
     * @param fileInfo
     * @param fileSupPath
     * @return 文件存储信息
     */
    public static FileStorage convert(ResourceFileStorageBO fileInfo, String fileSupPath) {
        FileStorage fileStorage = new FileStorage();
        fileStorage.setFileName(fileInfo.getResourceKey())
            .setSuperPath(fileSupPath)
            .setLength(fileInfo.getLength())
            .setDigest(fileInfo.getStrongChecksum())
            .setDigestType(DigestAlgorithm.MD5.getValue());
        return fileStorage;
    }

    //////////////////////////////////////////////////查询通用工具///////////////////////////////////////////////////////

    public static void addCondition(LambdaQueryChainWrapper<? extends Resource> query, LogicQuery<Integer> status, LogicQuery<Integer> integrity) {
        addStatusCondition(query, status);
        addStatusCondition(query, integrity);
    }

    public static void addCondition(LambdaQueryWrapper<? extends Resource> query, LogicQuery<Integer> status, LogicQuery<Integer> integrity) {
        addStatusCondition(query, status);
        addStatusCondition(query, integrity);
    }

    /**
     *
     */
    public static void addStatusCondition(LambdaQueryChainWrapper<? extends Resource> query, LogicQuery<Integer> status) {
        if (Objects.isNull(status) || status.isEmpty()) {
            return;
        }
        query.in(!status.eqs().isEmpty(), Resource::getStatus, status.eqs());
        query.notIn(!status.nes().isEmpty(), Resource::getStatus, status.nes());
    }

    /**
     *
     */
    public static void addStatusCondition(LambdaQueryWrapper<? extends Resource> query, LogicQuery<Integer> status) {
        if (Objects.isNull(status) || status.isEmpty()) {
            return;
        }
        query.in(!status.eqs().isEmpty(), Resource::getStatus, status.eqs());
        query.notIn(!status.nes().isEmpty(), Resource::getStatus, status.nes());
    }

    /**
     *
     */
    public static void addIntegrityCondition(LambdaQueryChainWrapper<ResourceFile> query, LogicQuery<Integer> integrity) {
        if (Objects.isNull(integrity) || integrity.isEmpty())
            return;
        query.in(!integrity.eqs().isEmpty(), ResourceFile::getIntegrity, integrity.eqs());
        query.notIn(!integrity.nes().isEmpty(), ResourceFile::getIntegrity, integrity.nes());
    }

    /**
     *
     */
    public static void addIntegrityCondition(LambdaQueryWrapper<ResourceFile> query, LogicQuery<Integer> integrity) {
        if (Objects.isNull(integrity) || integrity.isEmpty())
            return;
        query.in(!integrity.eqs().isEmpty(), ResourceFile::getIntegrity, integrity.eqs());
        query.notIn(!integrity.nes().isEmpty(), ResourceFile::getIntegrity, integrity.nes());
    }

}
